home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / mmdf / mmdf-IIb.43 / uip / ucbmail / lex.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-03-28  |  13.4 KB  |  724 lines

  1. /*
  2.  *  L E X . C 
  3.  *
  4.  *  EE/CIS Computer Lab
  5.  *  Department of Computer and Information Sciences
  6.  *  Department of Electrical Engineering
  7.  *  University of Delaware
  8.  *
  9.  *  REVISION HISTORY:
  10.  *
  11.  *  $Revision: 1.5 $
  12.  *
  13.  *  $Log:    lex.c,v $
  14.  * Revision 1.5  86/01/14  16:15:18  galvin
  15.  * Change the "mmdf.h" include line.
  16.  * 
  17.  * Revision 1.4  85/11/18  16:09:27  galvin
  18.  * Change name of command table to CmdTab so as not
  19.  * to conflict with cmdtab of MMDF.
  20.  * 
  21.  * Revision 1.3  85/11/18  14:34:21  galvin
  22.  * Teach setfile how to MMDF lock files.
  23.  * Have it use the access system call rather than opening and closing files.
  24.  * 
  25.  * Revision 1.2  85/11/18  12:35:29  galvin
  26.  * Added comment header for revision history.
  27.  * 
  28.  *
  29.  */
  30.  
  31. /*
  32.  * Copyright (c) 1980 Regents of the University of California.
  33.  * All rights reserved.  The Berkeley software License Agreement
  34.  * specifies the terms and conditions for redistribution.
  35.  */
  36.  
  37. #ifndef lint
  38. static char *sccsid = "@(#)lex.c    5.4 (Berkeley) 11/2/85";
  39. #endif not lint
  40.  
  41. #include "./rcv.h"
  42. #include "./mmdf.h"
  43. #include <sys/file.h>
  44. #include <sys/stat.h>
  45. #include <errno.h>
  46.  
  47. /*
  48.  * Mail -- a mail program
  49.  *
  50.  * Lexical processing of commands.
  51.  */
  52.  
  53. char    *prompt = "& ";
  54.  
  55. /*
  56.  * Set up editing on the given file name.
  57.  * If isedit is true, we are considered to be editing the file,
  58.  * otherwise we are reading our mail which has signficance for
  59.  * mbox and so forth.
  60.  */
  61.  
  62. setfile(name, isedit)
  63.     char *name;
  64. {
  65.     FILE *ibuf;
  66.     struct stat stb;
  67.     static int shudclob;
  68.     static char efile[128];
  69.     extern char tempMesg[];
  70.  
  71.     if ((ibuf = lk_fopen(name, "r", (char *)0, (char *)0, 5)) == NULL)
  72.         return(-1);
  73.     if (!edit) {
  74.     if (fstat(fileno(ibuf), &stb) < 0) {
  75.             perror(name);
  76.             exit(1);
  77.     }
  78.         if (stb.st_size == 0) {
  79.             lk_fclose(ibuf, name, (char *)0, (char *)0);
  80.         return(-1);
  81.     }
  82.     }
  83.  
  84.     /*
  85.      * Looks like all will be well.  We must now relinquish our
  86.      * hold on the current set of stuff.  Must hold signals
  87.      * while we are reading the new file, else we will ruin
  88.      * the message[] data structure.
  89.      */
  90.  
  91.     holdsigs();
  92.     if (shudclob)
  93.         if (edit)
  94.             edstop();
  95.         else
  96.             quit();
  97.  
  98.     /*
  99.      * Copy the messages into /tmp
  100.      * and set pointers.
  101.      */
  102.  
  103.     readonly = 0;
  104.     if (access(name, 2) < 0)
  105.         readonly++;
  106.     if (shudclob) {
  107.         fclose(itf);
  108.         fclose(otf);
  109.     }
  110.     shudclob = 1;
  111.     edit = isedit;
  112.     strncpy(efile, name, 128);
  113.     editfile = efile;
  114.     if (name != mailname)
  115.         strcpy(mailname, name);
  116.     mailsize = fsize(ibuf);
  117.     if ((otf = fopen(tempMesg, "w")) == NULL) {
  118.         perror(tempMesg);
  119.         lk_fclose(ibuf, name, (char *) 0, (char *) 0);
  120.         exit(1);
  121.     }
  122.     if ((itf = fopen(tempMesg, "r")) == NULL) {
  123.         perror(tempMesg);
  124.         lk_fclose(ibuf, name, (char *) 0, (char *) 0);
  125.         exit(1);
  126.     }
  127.     remove(tempMesg);
  128.     setptr(ibuf);
  129.     setmsize(msgCount);
  130.     lk_fclose(ibuf, name, (char *)0, (char *)0);
  131.     relsesigs();
  132.     sawcom = 0;
  133.     return(0);
  134. }
  135.  
  136. /*
  137.  * Interpret user commands one by one.  If standard input is not a tty,
  138.  * print no prompt.
  139.  */
  140.  
  141. int    *msgvec;
  142.  
  143. commands()
  144. {
  145.     int eofloop, shudprompt, stop();
  146.     register int n;
  147.     char linebuf[LINESIZE];
  148.     int hangup(), contin();
  149.  
  150. # ifdef VMUNIX
  151.     sigset(SIGCONT, SIG_DFL);
  152. # endif VMUNIX
  153.     if (rcvmode && !sourcing) {
  154.         if (sigset(SIGINT, SIG_IGN) != SIG_IGN)
  155.             sigset(SIGINT, stop);
  156.         if (sigset(SIGHUP, SIG_IGN) != SIG_IGN)
  157.             sigset(SIGHUP, hangup);
  158.     }
  159.     shudprompt = intty && !sourcing;
  160.     for (;;) {
  161.         setexit();
  162.  
  163.         /*
  164.          * Print the prompt, if needed.  Clear out
  165.          * string space, and flush the output.
  166.          */
  167.  
  168.         if (!rcvmode && !sourcing)
  169.             return;
  170.         eofloop = 0;
  171. top:
  172.         if (shudprompt) {
  173.             printf(prompt);
  174.             fflush(stdout);
  175. # ifdef VMUNIX
  176.             sigset(SIGCONT, contin);
  177. # endif VMUNIX
  178.         } else
  179.             fflush(stdout);
  180.         sreset();
  181.  
  182.         /*
  183.          * Read a line of commands from the current input
  184.          * and handle end of file specially.
  185.          */
  186.  
  187.         n = 0;
  188.         for (;;) {
  189.             if (readline(input, &linebuf[n]) <= 0) {
  190.                 if (n != 0)
  191.                     break;
  192.                 if (loading)
  193.                     return;
  194.                 if (sourcing) {
  195.                     unstack();
  196.                     goto more;
  197.                 }
  198.                 if (value("ignoreeof") != NOSTR && shudprompt) {
  199.                     if (++eofloop < 25) {
  200.                         printf("Use \"quit\" to quit.\n");
  201.                         goto top;
  202.                     }
  203.                 }
  204.                 if (edit)
  205.                     edstop();
  206.                 return;
  207.             }
  208.             if ((n = strlen(linebuf)) == 0)
  209.                 break;
  210.             n--;
  211.             if (linebuf[n] != '\\')
  212.                 break;
  213.             linebuf[n++] = ' ';
  214.         }
  215. # ifdef VMUNIX
  216.         sigset(SIGCONT, SIG_DFL);
  217. # endif VMUNIX
  218.         if (execute(linebuf, 0))
  219.             return;
  220. more:        ;
  221.     }
  222. }
  223.  
  224. /*
  225.  * Execute a single command.  If the command executed
  226.  * is "quit," then return non-zero so that the caller
  227.  * will know to return back to main, if he cares.
  228.  * Contxt is non-zero if called while composing mail.
  229.  */
  230.  
  231. execute(linebuf, contxt)
  232.     char linebuf[];
  233. {
  234.     char word[LINESIZE];
  235.     char *arglist[MAXARGC];
  236.     struct cmd *com;
  237.     register char *cp, *cp2;
  238.     register int c;
  239.     int muvec[2];
  240.     int edstop(), e;
  241.  
  242.     /*
  243.      * Strip the white space away from the beginning
  244.      * of the command, then scan out a word, which
  245.      * consists of anything except digits and white space.
  246.      *
  247.      * Handle ! escapes differently to get the correct
  248.      * lexical conventions.
  249.      */
  250.  
  251.     cp = linebuf;
  252.     while (any(*cp, " \t"))
  253.         cp++;
  254.     if (*cp == '!') {
  255.         if (sourcing) {
  256.             printf("Can't \"!\" while sourcing\n");
  257.             unstack();
  258.             return(0);
  259.         }
  260.         shell(cp+1);
  261.         return(0);
  262.     }
  263.     cp2 = word;
  264.     while (*cp && !any(*cp, " \t0123456789$^.:/-+*'\""))
  265.         *cp2++ = *cp++;
  266.     *cp2 = '\0';
  267.  
  268.     /*
  269.      * Look up the command; if not found, bitch.
  270.      * Normally, a blank command would map to the
  271.      * first command in the table; while sourcing,
  272.      * however, we ignore blank lines to eliminate
  273.      * confusion.
  274.      */
  275.  
  276.     if (sourcing && equal(word, ""))
  277.         return(0);
  278.     com = lex(word);
  279.     if (com == NONE) {
  280.         printf("Unknown command: \"%s\"\n", word);
  281.         if (loading)
  282.             return(1);
  283.         if (sourcing)
  284.             unstack();
  285.         return(0);
  286.     }
  287.  
  288.     /*
  289.      * See if we should execute the command -- if a conditional
  290.      * we always execute it, otherwise, check the state of cond.
  291.      */
  292.  
  293.     if ((com->c_argtype & F) == 0)
  294.         if (cond == CRCV && !rcvmode || cond == CSEND && rcvmode)
  295.             return(0);
  296.  
  297.     /*
  298.      * Special case so that quit causes a return to
  299.      * main, who will call the quit code directly.
  300.      * If we are in a source file, just unstack.
  301.      */
  302.  
  303.     if (com->c_func == edstop && sourcing) {
  304.         if (loading)
  305.             return(1);
  306.         unstack();
  307.         return(0);
  308.     }
  309.     if (!edit && com->c_func == edstop) {
  310.         sigset(SIGINT, SIG_IGN);
  311.         return(1);
  312.     }
  313.  
  314.     /*
  315.      * Process the arguments to the command, depending
  316.      * on the type he expects.  Default to an error.
  317.      * If we are sourcing an interactive command, it's
  318.      * an error.
  319.      */
  320.  
  321.     if (!rcvmode && (com->c_argtype & M) == 0) {
  322.         printf("May not execute \"%s\" while sending\n",
  323.             com->c_name);
  324.         if (loading)
  325.             return(1);
  326.         if (sourcing)
  327.             unstack();
  328.         return(0);
  329.     }
  330.     if (sourcing && com->c_argtype & I) {
  331.         printf("May not execute \"%s\" while sourcing\n",
  332.             com->c_name);
  333.         if (loading)
  334.             return(1);
  335.         unstack();
  336.         return(0);
  337.     }
  338.     if (readonly && com->c_argtype & W) {
  339.         printf("May not execute \"%s\" -- message file is read only\n",
  340.            com->c_name);
  341.         if (loading)
  342.             return(1);
  343.         if (sourcing)
  344.             unstack();
  345.         return(0);
  346.     }
  347.     if (contxt && com->c_argtype & R) {
  348.         printf("Cannot recursively invoke \"%s\"\n", com->c_name);
  349.         return(0);
  350.     }
  351.     e = 1;
  352.     switch (com->c_argtype & ~(F|P|I|M|T|W|R)) {
  353.     case MSGLIST:
  354.         /*
  355.          * A message list defaulting to nearest forward
  356.          * legal message.
  357.          */
  358.         if (msgvec == 0) {
  359.             printf("Illegal use of \"message list\"\n");
  360.             return(-1);
  361.         }
  362.         if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
  363.             break;
  364.         if (c  == 0) {
  365.             *msgvec = first(com->c_msgflag,
  366.                 com->c_msgmask);
  367.             msgvec[1] = NULL;
  368.         }
  369.         if (*msgvec == NULL) {
  370.             printf("No applicable messages\n");
  371.             break;
  372.         }
  373.         e = (*com->c_func)(msgvec);
  374.         break;
  375.  
  376.     case NDMLIST:
  377.         /*
  378.          * A message list with no defaults, but no error
  379.          * if none exist.
  380.          */
  381.         if (msgvec == 0) {
  382.             printf("Illegal use of \"message list\"\n");
  383.             return(-1);
  384.         }
  385.         if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
  386.             break;
  387.         e = (*com->c_func)(msgvec);
  388.         break;
  389.  
  390.     case STRLIST:
  391.         /*
  392.          * Just the straight string, with
  393.          * leading blanks removed.
  394.          */
  395.         while (any(*cp, " \t"))
  396.             cp++;
  397.         e = (*com->c_func)(cp);
  398.         break;
  399.  
  400.     case RAWLIST:
  401.         /*
  402.          * A vector of strings, in shell style.
  403.          */
  404.         if ((c = getrawlist(cp, arglist,
  405.                 sizeof arglist / sizeof *arglist)) < 0)
  406.             break;
  407.         if (c < com->c_minargs) {
  408.             printf("%s requires at least %d arg(s)\n",
  409.                 com->c_name, com->c_minargs);
  410.             break;
  411.         }
  412.         if (c > com->c_maxargs) {
  413.             printf("%s takes no more than %d arg(s)\n",
  414.                 com->c_name, com->c_maxargs);
  415.             break;
  416.         }
  417.         e = (*com->c_func)(arglist);
  418.         break;
  419.  
  420.     case NOLIST:
  421.         /*
  422.          * Just the constant zero, for exiting,
  423.          * eg.
  424.          */
  425.         e = (*com->c_func)(0);
  426.         break;
  427.  
  428.     default:
  429.         panic("Unknown argtype");
  430.     }
  431.  
  432.     /*
  433.      * Exit the current source file on
  434.      * error.
  435.      */
  436.  
  437.     if (e && loading)
  438.         return(1);
  439.     if (e && sourcing)
  440.         unstack();
  441.     if (com->c_func == edstop)
  442.         return(1);
  443.     if (value("autoprint") != NOSTR && com->c_argtype & P)
  444.         if ((dot->m_flag & MDELETED) == 0) {
  445.             muvec[0] = dot - &message[0] + 1;
  446.             muvec[1] = 0;
  447.             type(muvec);
  448.         }
  449.     if (!sourcing && (com->c_argtype & T) == 0)
  450.         sawcom = 1;
  451.     return(0);
  452. }
  453.  
  454. /*
  455.  * When we wake up after ^Z, reprint the prompt.
  456.  */
  457. contin()
  458. {
  459.  
  460.     printf(prompt);
  461.     fflush(stdout);
  462. }
  463.  
  464. /*
  465.  * Branch here on hangup signal and simulate quit.
  466.  */
  467. hangup()
  468. {
  469.  
  470.     holdsigs();
  471.     if (edit) {
  472.         if (setexit())
  473.             exit(0);
  474.         edstop();
  475.     }
  476.     else
  477.         quit();
  478.     exit(0);
  479. }
  480.  
  481. /*
  482.  * Set the size of the message vector used to construct argument
  483.  * lists to message list functions.
  484.  */
  485.  
  486. setmsize(sz)
  487. {
  488.  
  489.     if (msgvec != (int *) 0)
  490.         cfree((char *) msgvec);
  491.     msgvec = (int *) calloc((unsigned) (sz + 1), sizeof *msgvec);
  492. }
  493.  
  494. /*
  495.  * Find the correct command in the command table corresponding
  496.  * to the passed command "word"
  497.  */
  498.  
  499. struct cmd *
  500. lex(word)
  501.     char word[];
  502. {
  503.     register struct cmd *cp;
  504.     extern struct cmd CmdTab[];
  505.  
  506.     for (cp = &CmdTab[0]; cp->c_name != NOSTR; cp++)
  507.         if (isprefix(word, cp->c_name))
  508.             return(cp);
  509.     return(NONE);
  510. }
  511.  
  512. /*
  513.  * Determine if as1 is a valid prefix of as2.
  514.  * Return true if yep.
  515.  */
  516.  
  517. isprefix(as1, as2)
  518.     char *as1, *as2;
  519. {
  520.     register char *s1, *s2;
  521.  
  522.     s1 = as1;
  523.     s2 = as2;
  524.     while (*s1++ == *s2)
  525.         if (*s2++ == '\0')
  526.             return(1);
  527.     return(*--s1 == '\0');
  528. }
  529.  
  530. /*
  531.  * The following gets called on receipt of a rubout.  This is
  532.  * to abort printout of a command, mainly.
  533.  * Dispatching here when command() is inactive crashes rcv.
  534.  * Close all open files except 0, 1, 2, and the temporary.
  535.  * The special call to getuserid() is needed so it won't get
  536.  * annoyed about losing its open file.
  537.  * Also, unstack all source files.
  538.  */
  539.  
  540. int    inithdr;            /* am printing startup headers */
  541.  
  542. #ifdef _NFILE
  543. static
  544. _fwalk(function)
  545.     register int (*function)();
  546. {
  547.     register FILE *iop;
  548.  
  549.     for (iop = _iob; iop < _iob + _NFILE; iop++)
  550.         (*function)(iop);
  551. }
  552. #endif
  553.  
  554. static
  555. xclose(iop)
  556.     register FILE *iop;
  557. {
  558.     if (iop == stdin || iop == stdout ||
  559.         iop == stderr || iop == itf || iop == otf)
  560.         return;
  561.  
  562.     if (iop != pipef)
  563.         fclose(iop);
  564.     else {
  565.         pclose(pipef);
  566.         pipef = NULL;
  567.     }
  568. }
  569.  
  570. #ifndef VMUNIX
  571. stop(s)
  572. #else   VMUNIX
  573. stop()
  574. #endif  VMUNIX
  575. {
  576.  
  577. # ifndef VMUNIX
  578.     s = SIGINT;
  579. # endif VMUNIX
  580.     noreset = 0;
  581.     if (!inithdr)
  582.         sawcom++;
  583.     inithdr = 0;
  584.     while (sourcing)
  585.         unstack();
  586.     getuserid((char *) -1);
  587.  
  588.  
  589.     /*
  590.      * Before we go closing all them open FILEs, we need to
  591.          * kill off our friendly neighborhood submit process.
  592.      */
  593.     ml_end(OK);
  594.     /*
  595.      * Walk through all the open FILEs, applying xclose() to them
  596.      */
  597.     _fwalk(xclose);
  598.  
  599.     if (image >= 0) {
  600.         close(image);
  601.         image = -1;
  602.     }
  603.     fprintf(stderr, "Interrupt\n");
  604. # ifndef VMUNIX
  605.     signal(s, stop);
  606. # endif
  607.     reset(0);
  608. }
  609.  
  610. /*
  611.  * Announce the presence of the current Mail version,
  612.  * give the message count, and print a header listing.
  613.  */
  614.  
  615. char    *greeting    = "Mail version %s.  Type ? for help.\n";
  616.  
  617. announce(pr)
  618. {
  619.     int vec[2], mdot;
  620.     extern char *version;
  621.  
  622.     if (pr && value("quiet") == NOSTR)
  623.         printf(greeting, version);
  624.     mdot = newfileinfo();
  625.     vec[0] = mdot;
  626.     vec[1] = 0;
  627.     dot = &message[mdot - 1];
  628.     if (msgCount > 0 && !noheader) {
  629.         inithdr++;
  630.         headers(vec);
  631.         inithdr = 0;
  632.     }
  633. }
  634.  
  635. /*
  636.  * Announce information about the file we are editing.
  637.  * Return a likely place to set dot.
  638.  */
  639. newfileinfo()
  640. {
  641.     register struct message *mp;
  642.     register int u, n, mdot, d, s;
  643.     char fname[BUFSIZ], zname[BUFSIZ], *ename;
  644.  
  645.     for (mp = &message[0]; mp < &message[msgCount]; mp++)
  646.         if (mp->m_flag & MNEW)
  647.             break;
  648.     if (mp >= &message[msgCount])
  649.         for (mp = &message[0]; mp < &message[msgCount]; mp++)
  650.             if ((mp->m_flag & MREAD) == 0)
  651.                 break;
  652.     if (mp < &message[msgCount])
  653.         mdot = mp - &message[0] + 1;
  654.     else
  655.         mdot = 1;
  656.     s = d = 0;
  657.     for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) {
  658.         if (mp->m_flag & MNEW)
  659.             n++;
  660.         if ((mp->m_flag & MREAD) == 0)
  661.             u++;
  662.         if (mp->m_flag & MDELETED)
  663.             d++;
  664.         if (mp->m_flag & MSAVED)
  665.             s++;
  666.     }
  667.     ename = mailname;
  668.     if (getfold(fname) >= 0) {
  669.         strcat(fname, "/");
  670.         if (strncmp(fname, mailname, strlen(fname)) == 0) {
  671.             sprintf(zname, "+%s", mailname + strlen(fname));
  672.             ename = zname;
  673.         }
  674.     }
  675.     printf("\"%s\": ", ename);
  676.     if (msgCount == 1)
  677.         printf("1 message");
  678.     else
  679.         printf("%d messages", msgCount);
  680.     if (n > 0)
  681.         printf(" %d new", n);
  682.     if (u-n > 0)
  683.         printf(" %d unread", u);
  684.     if (d > 0)
  685.         printf(" %d deleted", d);
  686.     if (s > 0)
  687.         printf(" %d saved", s);
  688.     if (readonly)
  689.         printf(" [Read only]");
  690.     printf("\n");
  691.     return(mdot);
  692. }
  693.  
  694. /*
  695.  * Print the current version number.
  696.  */
  697.  
  698. pversion()
  699. {
  700.     printf("Version %s\n", version);
  701.     return(0);
  702. }
  703.  
  704. /*
  705.  * Load a file of user definitions.
  706.  */
  707. load(name)
  708.     char *name;
  709. {
  710.     register FILE *in, *oldin;
  711.  
  712.     if ((in = fopen(name, "r")) == NULL)
  713.         return;
  714.     oldin = input;
  715.     input = in;
  716.     loading = 1;
  717.     sourcing = 1;
  718.     commands();
  719.     loading = 0;
  720.     sourcing = 0;
  721.     input = oldin;
  722.     fclose(in);
  723. }
  724.